﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using Brain.Skinning;
using Microsoft.Xna.Framework;

namespace Brain
{
    public class Anim_Skin : IBaseAnimation
    {
        public bool Loop { get; set; }

        public int StartFrame { get; set; }

        public int AnimationEnd
        {
            get { return currentClip.Duration + StartFrame; }
        }

        /// <summary>
        /// The current reproducing Millisecond.
        /// </summary>
        public int CurrentFrame
        {
            get { return time; }
            protected set
            {
                time = value;
                UpdateAnimation();
            }
        }
        private int time = 0;

        /// <summary>
        /// The Skinned Model to Animate
        /// </summary>
        public SkinnedModel SkinnedModel { get; protected set; }

        public Anim_Skin(SkinnedModel model, SkinnedAnimationClip clip)
        {
            this.SkinnedModel = model;

            this.currentClip = clip;
        }

        public virtual void StartClip(SkinnedAnimationClip clip)
        {
            this.currentClip = clip;
            this.CurrentLoopFrame = 0;
        }

        private float CurrentLoopFrame;
        private SkinnedAnimationClip currentClip;
        public void Update(int TimeLineCurrentFrame)
        {
            CurrentFrame = TimeLineCurrentFrame;
        }

        public void UpdateLoop(float elapsed)
        {
            CurrentLoopFrame += elapsed;
            Update((int)CurrentLoopFrame);

            if (CurrentLoopFrame > AnimationEnd)
            {
                while (CurrentLoopFrame > AnimationEnd)
                {
                    CurrentLoopFrame -= AnimationEnd;
                }
            }
        }

        protected void UpdateAnimation()
        {
            UpdateRootBone();

            for (int i = 1; i < currentClip.Bones.Count; i++)
            {
                UpdateBone(i);
            }

            for (int i = 0; i < this.SkinnedModel.SkinTransforms.Length; i++)
            {
                SkinnedBone bone = SkinnedModel.SkinnedBones[i];
                this.SkinnedModel.SkinTransforms[i] = bone.InverseBindPose * this.SkinnedModel.SkinTransforms[i];
            }
        }

        protected void UpdateRootBone()
        {
            BoneKeyFrameCollection bone = currentClip.Bones[0];

            Matrix mat;
            InterpolateBetweenFrames(bone, out mat);
            this.SkinnedModel.SkinTransforms[0] = mat;
        }

        protected void UpdateBone(int index)
        {
            BoneKeyFrameCollection bone = currentClip.Bones[index];

            Matrix mat;
            InterpolateBetweenFrames(bone, out mat);
            mat *= this.SkinnedModel.SkinTransforms[bone.Bone.ParentBone];
            this.SkinnedModel.SkinTransforms[index] = mat;
        }

        protected void InterpolateBetweenFrames(BoneKeyFrameCollection bone, out Matrix mat)
        {
            int frameIndex;
            Frame_Skinned frame = bone.FindKeyFrameByTime(CurrentFrame, out frameIndex);
            int nextIndex = (frameIndex + 1) % bone.KeyFrames.Length;
            Frame_Skinned frame2 = bone.KeyFrames[nextIndex];

            float timeBetween;
            if (frameIndex == (bone.KeyFrames.Length - 1))
                timeBetween = this.currentClip.Duration - frame.Time;
            else
                timeBetween = frame2.Time - frame.Time;

            Quaternion finalOri;
            Vector3 finalPos;
            Vector3 finalSca;
            if (timeBetween > 0)
            {
                int elapsedKeyframeTime = this.CurrentFrame - frame.Time;
                float amount = elapsedKeyframeTime / (float)timeBetween;

                finalOri = Quaternion.Lerp(frame.Orientation, frame2.Orientation, amount);
                finalPos = Vector3.Lerp(frame.Position, frame2.Position, amount);
                finalSca = Vector3.Lerp(frame.Scale, frame2.Scale, amount);
            }
            else
            {
                finalOri = frame.Orientation;
                finalPos = frame.Position;
                finalSca = frame.Scale;
            }

            mat = Matrix.CreateFromQuaternion(finalOri);
            mat.Translation = finalPos;
            mat *= Matrix.CreateScale(finalSca);
        }



        public bool ShouldSerialize
        {
            get
            {
                throw new NotImplementedException();
            }
            set
            {
                throw new NotImplementedException();
            }
        }

        public void SerializeData(SerializationData parentData, int index)
        {
            throw new NotImplementedException();
        }

        public void DeserializeData(SerializationData parentData, int index)
        {
            throw new NotImplementedException();
        }


        public void SerializeData(SerializationData parentData, string add)
        {
            throw new NotImplementedException();
        }

        public void DeserializeData(SerializationData parentData, string add)
        {
            throw new NotImplementedException();
        }


        public void DeserializedAll()
        {
            throw new NotImplementedException();
        }
    }
}
